home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / a_utils / perl / jinx.lha / jinx < prev    next >
Text File  |  1993-08-13  |  55KB  |  1,824 lines

  1. #! /local/bin/perl
  2.  
  3. # jinx version 2.1 -- Copyright (c) 1990, Henk P. Penning.
  4. # You may distribute under the terms of the GNU General Public License
  5. # as specified in the README file that comes with the Jinx 2.1 kit.
  6.  
  7. sub QUIT
  8.   { if ( $#_ >= 0 )
  9.       { &move(0,0) ;
  10.         &addstr(@_[0]) ;
  11.     &clrtoeol ;
  12.         &frefresh ;
  13.       }
  14.     &quit() ;
  15.   }
  16.  
  17. sub quit
  18.   { if ( $#_ >= 0 )
  19.       { &showStatus(@_) ; }
  20.     &move($LINES-1,0) ;
  21.     &clrtoeol ;
  22.     &frefresh ;
  23.     &addlog("jinx closed.") ;
  24.     &endwin ;
  25.     &finishCterm ;
  26.     close(STDOUT) || warn "can't close STDOUT ($!)\n" ;
  27.     exit ;
  28.   }
  29.  
  30. sub showMenu
  31.   { local(*menu) = @_ ;
  32.     return if $oldMenu eq *menu ;
  33.     $oldMenu = *menu ;
  34.     local($i) ;
  35.     &clrreg($firstMenuLine,$lastMenuLine) ;
  36.     for ($i=0 ; $i <= $#menu ; $i++)
  37.       { &mvaddstr($firstMenuLine+$i,int(($COLS-length($menu[$i]))/2),
  38.                   $menu[$i]) ;
  39.       }
  40.     &frefresh ;
  41.   }
  42.  
  43. sub ch2key
  44.   { local($res) = @_ ;
  45.     $res = &ch2str($res) ;
  46.     if ( defined($keymap{$res}) )
  47.       { return $keymap{$res} ; }
  48.     else
  49.       { return $res ; }
  50.   }
  51.  
  52. sub getMappedKey { return &ch2key(&getchintR) ; }
  53.  
  54. sub showHelp
  55.   { local(*menu) = @_ ;
  56.     &showCommand('help on (RET to quit):',' ') ;
  57.     for ( $res = &getMappedKey
  58.     ; $res ne 'KEY_RET' && $res ne ' '
  59.     ; $res = &getMappedKey
  60.     )
  61.       { $res = defined($menu{$res})
  62.                ? ( defined($menu{"help_$res"})
  63.                  ? $menu{"help_$res"} : 'no help'
  64.                  )
  65.                : "not on the menu ($res)" ;
  66.         &showStatus($res) ;
  67.         &move($commandLine,$curposCommand) ;
  68.       }
  69.   }
  70.  
  71. sub showChoice
  72.   { local(*menu,$default,$command) = @_ ;
  73.     local($res) ;
  74.     $command = "now what:" if $command eq '' ;
  75.     &showMenu(*menu) ;
  76.     &showCommand($command,$menu{$default}) ;
  77.     while ( 1 )
  78.       { $res = &getMappedKey ;
  79.         &showStatus('') ;
  80.         $res = $default if $res eq 'KEY_RET' ;
  81.         last if defined $menu{$res} ;
  82.         if ( $res eq '?' )
  83.           { &showHelp(*menu) ;
  84.             &showCommand($command,$menu{$default}) ;
  85.             next ;
  86.           }
  87.         &showStatusBeep("not on the menu ($res)") ;
  88.         &move($commandLine,$curposCommand) ;
  89.         &frefresh ;
  90.       }
  91.     $res = $menu{'KEY_TAB'} if $res eq 'KEY_TAB' ;
  92.     &addCommand($menu{$res}) if $res ne $default ;
  93.     return $res ;
  94.   }
  95.  
  96. sub showStatusList
  97.   { local($_) ;
  98.     &showStatus(shift) if $#_ >= 0 ;
  99.     for ( @_ )
  100.       { &showCommand('hit any key to continue: ') ;
  101.         &getchintR ;
  102.         &showStatus($_) ;
  103.       }
  104.     &frefresh ;
  105.   }
  106.  
  107. sub showStatus
  108.   { local($_) = join('',@_) ;
  109.     return if $oldStatus eq $_ ;
  110.     if ( $_[0] eq '' )
  111.       { &mvaddstr($statusLine,0,'>') ;
  112.         &clrtoeol ;
  113.       }
  114.     else
  115.       { &show($_,0,$statusLine,0,$COLS-2,'> > ') ; }
  116.     $oldStatus = $_ ;
  117.     &frefresh ;
  118.   }
  119.  
  120. sub showStatusBeep
  121.   { &showStatus ;
  122.     &beep ;
  123.   }
  124.  
  125. sub showJinxD
  126.   { local($marked) = @_ ;
  127.     local($dbName,$mod) ;
  128.     $dbName = ($db ne '') ? $db : (($#name < 0) ? 'none' : 'no name') ;
  129.     $mod = ($#name < 0) ? '' : ($dirty ? ' [modified]' : ' [not modified]') ;
  130.     $marked = ( $marked > 0 ) ? " marked $marked" : '' ;
  131.     local($str) = "database: " . $dbName . $mod . $marked ;
  132.     if ( $str ne $oldJinxD )
  133.       { &show($str,0,$jinxLineD,0,$COLS-2,'> > ') ;
  134.         $oldJinxD = $str ;
  135.       }
  136.   }
  137.  
  138. sub showJinxR
  139.   { local($numRecs,$markInfo) = @_ ;
  140.     local($str) ;
  141.     if ( $numRecs >= 0 )
  142.       { $str = sprintf("record %d of %d",$curr+1,$numRecs+1) ;
  143.         $str .= " $markInfo" if $markInfo ne '' ;
  144.       }
  145.     else
  146.       { $str = 'no data' ; }
  147.     if ( $str ne $showJinxR )
  148.       { &show($str,0,$jinxLineR,0,$COLS-2,'> > ') ;
  149.         $showJinxR = $str ;
  150.       }
  151.   }
  152.  
  153. sub showCommand
  154.   { &mvaddstr($commandLine,0,join(' ',@_)) ;
  155.     &clrtoeol ;
  156.     $curposCommand = length($_[0])+1  ;
  157.     &move($commandLine,$curposCommand) if $#_ > 0 ;
  158.     &frefresh ;
  159.   }
  160.  
  161. sub addCommand
  162.   { &mvaddstr($commandLine,$curposCommand,$_[0]) ;
  163.     &clrtoeol ;
  164.     &frefresh ;
  165.   }
  166.  
  167. sub checkDirty
  168.   { return 1 if $dirty == 0 ;
  169.     local($dbname) = substr($db,rindex($db,'/')+1,1000) ;
  170.     local($times) = ($dirty == 1) ? 'once' : "$dirty times" ;
  171.     &showStatusBeep( (($db eq '') ? 'db' : $dbname) . " modified $times") ;
  172.     if ( &showChoice(*dirtyMenu,'n',"$command anyway?") eq 'y' )
  173.       { return 1 ; }
  174.     else
  175.       { &showStatus('no change') ;
  176.         $checkDirty = 1 ;
  177.         return 0 ;
  178.       }
  179.   }
  180.  
  181. sub doDescrMenu
  182.   { local(*descr) = @_ ;
  183.     local(@name,@pat,@record) ;
  184.     local($res,$res1,$newPat,$next) = 'x' ;
  185.  
  186.     &splitDescr(*descr,*name,*pat) ;
  187.     @record = @pat ;
  188.  
  189.     while ( 1 )
  190.       { $res = &showChoice(*doDescrMenu,$res) ;
  191.         if ( $res eq 'x' )
  192.           { return 0 ; }
  193.         elsif ( $res eq 'f' )
  194.           { &mkDescr(*descr,*name,*pat) ;
  195.             return 1 ;
  196.           }
  197.         elsif ( $res eq 'KEY_DOWN' && $curF < $#name )
  198.           { $curF++ ; }
  199.         elsif ( $res eq 'KEY_UP' && $curF > 0 )
  200.           { $curF-- ; }
  201.         elsif ( $res eq 'p' )
  202.           { $res1 = &doUpdate(*updReMenu,'user',1,
  203.                               *testRe,*quitUpd,*retPat) ;
  204.             if ( $res1 eq 'a' )
  205.               { @pat = @record ; }
  206.             else
  207.               { &showStatus('no change') ;
  208.                 @record = @pat ;
  209.               }
  210.           }
  211.         elsif ( $res eq 'n' )
  212.           { @record = @name ;
  213.             &unshowValues ;
  214.             &showRecord(*name,*record,$#data) ;
  215.             $res1 = &doUpdate(*updReMenu,'user',1,
  216.                               *testNames,*quitUpd,*retNames) ;
  217.             if ( $res1 eq 'a' )
  218.               { @name = @record ; }
  219.             else
  220.               { &showStatus('no change') ; }
  221.             @record = @pat ;
  222.             &unshowRecord ;
  223.           }
  224.         else
  225.           { &showStatusBeep("can't do it") ; }
  226.         &showRecord(*name,*record,$#data) ;
  227.       }
  228.   }
  229.  
  230. sub getNames
  231.   { local(*name) = @_ ;
  232.     local($res,$cnt,@record,@res,$_,$i) = ('f',1) ;
  233.  
  234.     @record = &emptyRecord($#name) ;
  235.     $Xr = 0 ;
  236.     $curF = 0 ;
  237.     &unshowValues ;
  238.     &showRecord(*name,*record,$#data) ;
  239.  
  240.     while ( 1 )
  241.       { $res = &showChoice(*getNamesMenu,$res) ;
  242.         if ( $res eq 'x' )
  243.           { return 0 ; }
  244.         elsif ( $res eq 'f' )
  245.           { @res = () ;
  246.             $i = 0 ;
  247.             for ( @record )
  248.               { $res[$_-1] = $i if $_ > 0 ;
  249.                 $i++ ;
  250.               }
  251.             return 1, @res ;
  252.           }
  253.         elsif ( $res eq 'KEY_DOWN' && $curF < $#name )
  254.           { $curF++ ; }
  255.         elsif ( $res eq 'KEY_UP' && $curF > 0 )
  256.           { $curF-- ; }
  257.         elsif ( $res eq 'KEY_RIGHT' && $record[$curF] == 0 )
  258.           { $record[$curF] = $cnt++ ;
  259.             &showValue($record[$curF],$curF) ;
  260.           }
  261.         elsif ( $res eq 'KEY_LEFT' && $record[$curF] != 0 )
  262.           { $cnt-- ;
  263.             if ( $record[$curF] == $cnt )
  264.               { &showValue('',$curF) ; }
  265.             else
  266.               { for ( @record )
  267.                  { $_-- if $_ != 0 && $_ > $record[$curF] ; }
  268.                 &unshowValues ;
  269.               }
  270.             $record[$curF] = '' ;
  271.           }
  272.         elsif ( $res eq 'd' && $record[$curF] > 1 )
  273.           { $i = 0 ;
  274.             for ( @record )
  275.               { if ( $_ == $record[$curF] - 1 )
  276.                   { $_++ ; &showValue($_,$i) ; }
  277.                 $i++ ;
  278.               }
  279.             &showValue(--$record[$curF],$curF) ;
  280.           }
  281.         elsif ( $res eq 'i' && $record[$curF] < $cnt-1 && $record[$curF] > 0 )
  282.           { $i = 0 ;
  283.             for ( @record )
  284.               { if ( $_ == $record[$curF] + 1 )
  285.                   { $_-- ; &showValue($_,$i) ; }
  286.                 $i++ ;
  287.               }
  288.             &showValue(++$record[$curF],$curF) ;
  289.           }
  290.         elsif ( $res eq 's' && $cnt > 1 )
  291.           { $i = 0 ;
  292.             @res = &emptyRecord($#name) ;
  293.             for ( @record )
  294.               { if ( $_ > 0 )
  295.                   { $res[$_-1] = $name[$i] ; }
  296.                 $i++ ;
  297.               }
  298.             &unshowValues ;
  299.             &showRecord(*name,*res,$#data) ;
  300.             &showCommand('hit any key to continue: ') ;
  301.             &getchintR ;
  302.             &unshowValues ;
  303.           }
  304.         else
  305.           { &showStatusBeep("can't do it") ; }
  306.         &showRecord(*name,*record,$#data) ;
  307.       }
  308.   }
  309.  
  310. sub getFieldDescr
  311.   { local($fName,$fPat,$fVal,$res) ;
  312.  
  313.     ($res,$fName) = &getStrFrom("fieldname:",*fName,$#fName) ;
  314.     if ( $res == 0 || $fName eq '' )
  315.       { return '', "no name ; no change" ; }
  316.     elsif ( $fName !~ /$namePat/ )
  317.       { return '', "name '$fName' not alfa-numeric ; no change" ; }
  318.     elsif ( grep(/^$fName$/,@name) != 0 )
  319.       { return '', "name '$fName' already used ; no change" ; }
  320.  
  321.     ($res,$fPat) = &getStrFrom("pattern:",*sPat,$#sPat) ;
  322.     if ( $res == 0 )
  323.       { return '', 'no change' ; }
  324.     elsif ( ! &testPat($fPat) )
  325.       { return '', "$@ ; no change" ; }
  326.  
  327.     ($res,$fVal) = &getStrFrom("default value:",*fVal,$#fVal) ;
  328.     if ( $res == 0 )
  329.       { return '', 'no change' ; }
  330.     elsif ( $fPat && $fVal !~ /$fPat/ )
  331.       { return '', "default '$fVal' doesn't match '$fPat' ; no change" ; }
  332.  
  333.     return $fName, $fPat, $fVal ;
  334.   }
  335.  
  336. sub searchNext
  337.   { local(*row,$from,$sPat) = @_ ;
  338.     local($i) ;
  339.  
  340.     &showStatus("searching ($sPat) ...") ;
  341.     for ($i=$from+1 ; $i<=$#row && $row[$i] !~ /$sPat/ ; $i++) { ; } ;
  342.     return($i,'found')           if $i <= $#row ;
  343.     for ($i=0 ; $i<=$from && $row[$i] !~ /$sPat/ ; $i++) { ; } ;
  344.     return($i,'found only here') if $i == $from ;
  345.     return($i,'found')           if $i <  $from ;
  346.     return($from,'not found') ;
  347.   }
  348.  
  349. sub searchNextRecord
  350.   { local(*row,$fromR,*record,$fromF,$sPat) = @_ ;
  351.     local($i,$j,@rec1) ;
  352.  
  353.     &showStatus("searching ($sPat) ...") ;
  354.     for ($j=$fromF+1 ; $j<=$#record && $record[$j] !~ /$sPat/ ; $j++) { ; } ;
  355.     return($fromR,$j,'found in this record') if $j <= $#record ;
  356.     for ($i=$fromR+1 ; $i<=$#row && $row[$i] !~ /$sPat/ ; $i++) { ; } ;
  357.     if ( $i <= $#row )
  358.       { @rec1 = split($;,$row[$i],$#record+1) ;
  359.     for ($j=0 ; $j<=$#rec1 && $rec1[$j] !~ /$sPat/ ; $j++) { ; } ;
  360.     return($i,$j,'found') if $j <= $#rec1 ;
  361.     return($i,$fromF,"error: record ($i) but no field in tail") ;
  362.       }
  363.     for ($i=0 ; $i<=$fromR && $row[$i] !~ /$sPat/ ; $i++) { ; } ;
  364.     return($fromR,$fromF,'not found') if $i > $fromR ;
  365.     @rec1 = split($;,$row[$i],$#record+1) ;
  366.     for ($j=0 ; $j<=$#rec1 && $rec1[$j] !~ /$sPat/ ; $j++) { ; } ;
  367.     return($i,$fromF,"error: record ($i) but no field in head") if $j > $#rec1 ;
  368.     return($i,$j,'found')                     if $i != $fromR ;
  369.     return($i,$j,'found only in this record') if $j != $fromF ;
  370.     return($i,$j,'found only here')           if $j == $fromF ;
  371.   }
  372.  
  373. sub searchNextIndRecord
  374.   { local(*ind,*row,$fromR,*record,$fromF,$sPat) = @_ ;
  375.     local($i,$j,@rec1) ;
  376.  
  377.     &showStatus("searching ($sPat) ...") ;
  378.     for ($j=$fromF+1 ; $j<=$#record && $record[$j] !~ /$sPat/ ; $j++) { ; } ;
  379.     return($fromR,$j,'found in this record') if $j <= $#record ;
  380.     for ($i=$fromR+1 ; $i<=$#ind && $row[$ind[$i]] !~ /$sPat/ ; $i++) { ; } ;
  381.     if ( $i <= $#ind )
  382.       { @rec1 = split($;,$row[$ind[$i]],$#record+1) ;
  383.     for ($j=0 ; $j<=$#rec1 && $rec1[$j] !~ /$sPat/ ; $j++) { ; } ;
  384.     return($i,$j,'found') if $j <= $#rec1 ;
  385.     return($i,$fromF,"error: record ($i) but no field in tail") ;
  386.       }
  387.     for ($i=0 ; $i<=$fromR && $row[$ind[$i]] !~ /$sPat/ ; $i++) { ; } ;
  388.     return($fromR,$fromF,'not found') if $i > $fromR ;
  389.     @rec1 = split($;,$row[$ind[$i]],$#record+1) ;
  390.     for ($j=0 ; $j<=$#rec1 && $rec1[$j] !~ /$sPat/ ; $j++) { ; } ;
  391.     return($i,$fromF,"error: record ($i) but no field in head") if $j > $#rec1 ;
  392.     return($i,$j,'found')                     if $i != $fromR ;
  393.     return($i,$j,'found only in this record') if $j != $fromF ;
  394.     return($i,$j,'found only here')           if $j == $fromF ;
  395.   }
  396.  
  397. sub setDataYXd
  398.   { local($numNames) = @_ ;
  399.     if ( $curr < $numFieldLines )
  400.       { $Yd = 0 ; }
  401.     elsif ( $curr > $#data - $numFieldLines )
  402.       { $Yd = $#data - $numFieldLines + 1 ; }
  403.     else
  404.       { $Yd = $curr - int(($numFieldLines-1)/2) ; }
  405.     if ( $colView )
  406.       { $maxXd = $numNames ; }
  407.     else
  408.       { $maxStrlenD = &maxStrlen(*data,$Yd,$Yd+$numFieldLines-1) ;
  409.         $maxXd = $maxStrlenD - $numInfoCols + 2 ;
  410.       }
  411.     $Xd = &max(0,&min($maxXd,$Xd)) ;
  412.   }
  413.  
  414. sub unshowData
  415.   { $oldYd = undef ; }
  416.  
  417. sub showData
  418.   { local(*data,$numNames) = @_ ;
  419.     local($i) ;
  420.     &showJinxR($#data) ;
  421.     if ( ! defined($oldYd) || $Yd > $curr || $curr >= $Yd + $numFieldLines )
  422.       { &setDataYXd($numNames) ; }
  423.     if ( ! defined($oldYd) )
  424.       { &setDataScreen(*data) ;
  425.         &showDataScreen(*data,$Yd,$Xd,$curr) ;
  426.       }
  427.     elsif ( $Yd != $oldYd || $Xd != $oldXd )
  428.       { &showDataScreen(*data,$Yd,$Xd,$curr) ; }
  429.     elsif ( $#data >= 0 )
  430.       { &moveCursorScreen($curr,$Yd) ; }
  431.     $oldYr = undef ;
  432.     $oldYd = $Yd ;
  433.     $oldXd = $Xd ;
  434.     &frefresh ;
  435.   }
  436.  
  437. sub setYr
  438.   { if ( $curF < $numFieldLines )
  439.       { $Yr = 0 ; }
  440.     elsif ( $curF > $#record - $numFieldLines )
  441.       { $Yr = $#record - $numFieldLines + 1 ; }
  442.     else
  443.       { $Yr = $curF - int(($numFieldLines-1)/2) ; }
  444.   }
  445.  
  446. sub setXr
  447.   { @strlenR = @record ;
  448.     $maxStrlenR = &max(grep($_=length,@strlenR)) ;
  449.     $maxXr = $maxStrlenR - $numValueCols + 2 ;
  450.     $Xr = &max(0,&min($Xr,$maxXr)) ;
  451.   }
  452.  
  453. sub unshowRecord
  454.   { $oldYr = undef ; }
  455.  
  456. sub unshowValues
  457.   { $oldXr = undef ; }
  458.  
  459. sub showValue
  460.   { local($value,$curF) = @_ ;
  461.     &showValueScreen($value,$curF,$Yr) ;
  462.   }
  463.  
  464. sub editValue
  465.   { local($newField,$curF) = @_ ;
  466.     local(@res) ;
  467.     @res = &editValueScreen($newField,$curF,$Yr) ;
  468.     return @res ;
  469.   }
  470.  
  471. sub showRecord
  472.   { local(*name,*record,$numRecs,$markInfo) = @_ ;
  473.     &showJinxR($numRecs,$markInfo) ;
  474.     if ( ! defined($oldYr) || $Yr > $curF || $curF >= $Yr + $numFieldLines )
  475.       { &setYr ; }
  476.     if ( ! defined($oldYr) )
  477.       { &setKeyScreen(*name) ;
  478.         &showKeysScreen(*name,$Yr,0,$curF) ;
  479.         &setXr ;
  480.         &showValuesScreen(*record,$Yr,$Xr) ;
  481.       }
  482.     elsif ( $Yr != $oldYr )
  483.       { &showKeysScreen(*name,$Yr,0,$curF) ;
  484.         &setXr ;
  485.         &showValuesScreen(*record,$Yr,$Xr) ;
  486.       }
  487.     else
  488.       { if ( ! defined($oldXr) || $Xr != $oldXr )
  489.           { &setXr ;
  490.             &showValuesScreen(*record,$Yr,$Xr) ;
  491.           }
  492.         if ( $curF != $oldCurF )
  493.           { &moveCursorScreen($curF,$Yr) ; }
  494.       }
  495.     $oldYd = undef ;
  496.     $oldYr = $Yr ;
  497.     $oldXr = $Xr ;
  498.     $oldCurF = $curF ;
  499.     &frefresh ;
  500.   }
  501.  
  502. sub showTest
  503.   { local(*name,*pat,*record) = @_ ;
  504.     local($_,@res,$i) ;
  505.     @res = &doTest(*record,*pat) ;
  506.     do { &showStatus("ok") ; return ; } if $#res < 0 ;
  507.     for ( @res )
  508.       { $curF = $_ ;
  509.         &showRecord(*name,*record,$#data) ;
  510.         &showStatusBeep("field doesn't match $pat[$_]") ;
  511.         last if $i++ == $#res ;
  512.         &showCommand('hit any key to continue: ') ;
  513.         &getchintR ;
  514.       }
  515.   }
  516.  
  517. sub showTestAll
  518.   { local(*name,*pat,*data) = @_ ;
  519.     local($c,$tmpCurr,@record,@res) = 'n' ;
  520.     $tmpCurr = $curr ;
  521. showTestAll:
  522.     for $curr ( $tmpCurr..$#data,0..$tmpCurr-1 )
  523.       {
  524. showTest:
  525.         while ( 1 )
  526.           { @record = split(/$;/,$data[$curr],$#name+1) ;
  527.             @res = &doTest(*record,*pat) ;
  528.             next showTestAll if $#res < 0 ;
  529.  
  530.             &unshowValues ;
  531. showField:  for ( @res )
  532.               { $curF = $_ ;
  533.                 &showRecord(*name,*record,$#data) ;
  534.                 &showStatusBeep("field doesn't match $pat[$_]") ;
  535.                 $c = &showChoice(*testMenu,$c) ;
  536.                 last showField if $c =~ /[quN]/ ;
  537.               }
  538.  
  539.             if ( $c eq 'x')
  540.               { &showStatus('exit Test All') ;
  541.                 return $curr ;
  542.               }
  543.             elsif ( $c eq 'u' )
  544.               { $c = &doUpdate(*updMatchMenu,'user',1,
  545.                                *testUpd,*quitUpd,*retUpd) ;
  546.                 if ( $c eq 'a' )
  547.                   { $data[$curr] = $record ;
  548.                     $dirty++ ;
  549.                     &showJinxD() ;
  550.                   }
  551.                 $c = 'n' ;
  552.                 redo showTest ;
  553.               }
  554.             else
  555.               { last showTest ; }
  556.           }
  557.         &showStatus('Testing All ...') ;
  558.       }
  559.     &showStatus('tested all') ;
  560.     return $curr ;
  561.   }
  562.  
  563. sub getStrFrom
  564.   { local($command,*alts,$default) = @_ ;
  565.     local($x,$str,$ret) ;
  566.  
  567.     $default = $#alts if $#_ < 2 ;
  568.     push(@alts,'') ;
  569.     $default = &max(0,&min($default,$#alts)) ;
  570.  
  571.     &showMenu(*searchMenu) ;
  572.     $command .= ' ' ;
  573.     $x = length($command) ;
  574.     &showCommand($command) ;
  575.  
  576.     while ( 1 )
  577.       { $str = $alts[$default] ;
  578.         ($str,$res) = &edit($str,0,$commandLine,$x,$COLS-$x-2,'|<>|') ;
  579. # replace previous line by next line if LAST-ROW-COL-BUG         |
  580. #       ($str,$res) = &edit($str,0,$commandLine,$x,$COLS-$x-3,'|<>|') ;
  581.         $res = &ch2key($res) ;
  582.         if ( $res eq 'KEY_TAB' )
  583.           { $ret = 0 ; $str = '' ; last ; }
  584.         elsif ( $res eq 'KEY_RET' )
  585.           { $ret = 1 ; last ; }
  586.         elsif ( $res eq 'KEY_UP' && $default > 0 )
  587.           { $default-- ; }
  588.         elsif ( $res eq 'KEY_DOWN' && $default < $#alts )
  589.           { $default++ ; }
  590.         else
  591.           { &showStatusBeep("can't do $res") ; }
  592.       }
  593.     pop(@alts) while $#alts >= 0 && ! $alts[$#alts] ;
  594.     push(@alts,$str) if $str && ( $#alts < 0 || $str ne $alts[$#alts] ) ;
  595.     return $ret, $str ;
  596.   }
  597.  
  598. sub getGoto
  599.   { local($curr) = @_ ;
  600.     local($res,$goto) = &getStrFrom('Where:',*gotos,$#gotos) ;
  601.     return '' unless $res ;
  602.     $goto .= '' ;
  603.     return $goto-1  if $goto =~ /^[0-9]+$/ ;
  604.     return $curr+$1 if $goto =~ /^\+([0-9]+)$/ ;
  605.     return $curr-$1 if $goto =~ /^\-([0-9]+)$/ ;
  606.     return '' ;
  607.   }
  608.  
  609. sub doPeek
  610.   { local(*name,$db,*name1,*data1) = @_ ;
  611.  
  612.     local($_,@vals,@common,@common1,@new,@empty) ;
  613.  
  614.     &mkInvert(*name) ;
  615.     &mkInvert(*name1) ;
  616.     for ( @name1 )
  617.       { if ( defined $name{$_} )
  618.       { push(@common,$name{$_}) ;
  619.         push(@common1,$name1{$_}) ;
  620.           }
  621.     else
  622.       { $_ = '__' . $_ ; }
  623.       }
  624.     
  625.     return(0,"no field in common ; no change") if $#common < 0 ;
  626.     $_ = ( $#common ? 'fields' : 'field' ) ;
  627.     &showStatus("relevant $_ : " . join(',',@name1[@common1]) ) ;
  628.  
  629.     @vals = 0..$#data1 ;
  630.     $db = "Peeking in " . substr($db,rindex($db,'/')+1,1000) ;
  631.     ($res,@vals) = &selectFrom($db,*name1,*data1,@vals) ;
  632.     if ( $res eq 'x' || $#vals < 0 )
  633.       { return(0,'no change') ; }
  634.     else
  635.       { @empty = &emptyRecord($#name) ;
  636.     for ( @vals )
  637.       { @new = @empty ;
  638.         @new[@common] = (split(/$;/,$data1[$_],$#name1+1))[@common1] ;
  639.         push(@data,join($;,@new)) ;
  640.       }
  641.     $_ = $#vals+1 ;
  642.     $_ = ( $_ == 1 ) ? 'one record' : "$_ records" ;
  643.     return(1,"added $_ (at the end)") ;
  644.       }
  645.   }
  646.  
  647. sub doRead
  648.   { local(*name,$db,*name1,*data1) = @_ ;
  649.  
  650.     local($_,$i,@vals,@common,@common1,@new,@empty,%key,%key1,$key1) ;
  651.  
  652.     &mkInvert(*name) ;
  653.     &mkInvert(*name1) ;
  654.     for ( @name1 )
  655.       { if ( defined $name{$_} )
  656.       { push(@common,$name{$_}) ;
  657.         push(@common1,$name1{$_}) ;
  658.           }
  659.     else
  660.       { $_ = '__' . $_ ; }
  661.       }
  662.     
  663.     return(0,"no field in common ; no change") if $#common < 0 ;
  664.  
  665.     for ( @data )
  666.       { $key{join($;,(split(/$;/,$_))[@common])} = 1 ; }
  667.  
  668.     $i = 0 ;
  669.     for ( @data1 )
  670.       { $key1 = join($;,(split(/$;/))[@common1]) ;
  671.     push(@vals,$i) unless defined($key{$key1}) || defined($key1{$key1}) ;
  672.         $key1{$key1}++ ;
  673.         $i++ ;
  674.       }
  675.  
  676.     return(0,"no new records to Read ; no change") if $#vals < 0 ;
  677.  
  678.     $_ = ( $#common ? 'fields' : 'field' ) ;
  679.     &showStatus("relevant $_ : " . join(',',@name1[@common1]) ) ;
  680.  
  681.     $db = "Reading in " . substr($db,rindex($db,'/')+1,1000) ;
  682.     ($res,@vals) = &selectFrom($db,*name1,*data1,@vals) ;
  683.     if ( $res eq 'x' || $#vals < 0 )
  684.       { return(0,'no change') ; }
  685.     else
  686.       { @empty = &emptyRecord($#name) ;
  687.     for ( @vals )
  688.       { @new = @empty ;
  689.         @new[@common] = (split(/$;/,$data1[$_],$#name1+1))[@common1] ;
  690.         push(@data,join($;,@new)) ;
  691.       }
  692.     $_ = $#vals+1 ;
  693.     $_ = ( $_ == 1 ) ? 'one record' : "$_ records" ;
  694.     return(1,"added $_ (at the end)") ;
  695.       }
  696.   }
  697.  
  698. # doKeyTest returns 0 if error or exit
  699. # doKeyTest returns 1 if all uniq
  700. # doKeyTest returns x if x+1 doubles appeared
  701. sub doKeyTest
  702.   { local(*data,*name,*fields) = @_ ;
  703.  
  704.     local($_,$i,$res,@dvals,@vals,$vals,@delete,%key,@prefix) ;
  705.     local(@names) = @name ;
  706.     local($ret) = 1 ;
  707.  
  708.     if ( $#fields >= 0 )
  709.       { for ( 0..$#names ) { $prefix[0+$_] = '__' ; }
  710.         for ( @fields )    { $prefix[0+$_] = '' ; }
  711.     $i = 0 ;
  712.         for ( @names )     { $_ = $prefix[$i++] . $_ ; }
  713.     $i = 0 ;
  714.         for ( @data )
  715.           { $key{join($;,(split(/$;/,$_))[@fields])} .= ( $i++ . ',' ) ; }
  716.       }
  717.     else
  718.       { for ( @data ) { $key{$_} .= ( $i++ . ',' ) ; }
  719.     @fields = 0..$#names ;
  720.       }
  721.  
  722.     local($db) = "SELECT TO DELETE" ;
  723. keyTest:
  724.     for ( sort keys %key )
  725.       { $vals = $key{$_} ;
  726.     chop $vals ;
  727.     next keyTest unless $vals =~ /,/ ; 
  728.     $ret++ ;
  729.     next keyTest if $res eq 'f' ; 
  730.     @vals = split(/,/,$vals) ;
  731. keyTestMenu:
  732.     while ( 1 )
  733.       { &showStatus($#vals+1, " records have key '", &intext($_), "'") ;
  734.         $res = &showChoice(*keyTestMenu,'n') ;
  735.         if ( $res eq 's' )
  736.           { ($res,@dvals) = &selectFrom($db,*names,*data,@vals) ;
  737.                 if ( $res eq 'x' )
  738.                   { next keyTestMenu ; }
  739.                 elsif ( $res eq 'a' )
  740.                   { push(@delete,@dvals) ;
  741.             next keyTest ;
  742.           }
  743.                 else
  744.                   { &showStatus('Huh ?') ; }
  745.           }
  746.             elsif ( $res eq 'n' )
  747.               { next keyTest ; }
  748.             elsif ( $res eq 'f' )
  749.               { next keyTest ; }
  750.             elsif ( $res eq 'x' )
  751.               { @delete = () ;
  752.         $ret = 0 ; ;
  753.         last keyTest ;
  754.           }
  755.             else
  756.               { &showStatus('Huh ?') ; }
  757.       }
  758.       }
  759.     &unshowRecord ;
  760.     return $ret, @delete ;
  761.   }
  762.  
  763. sub selectFrom
  764. { local($db,*name2,*data2,@vals) = @_ ;
  765.   local($c,$curr,$Yr,$Xr,$maxStrlenR,$curF,$dirty,$i) ;
  766.   local($marked,@sub,@unmarked,%marked,$markInfo) ;
  767.   local($res,$res1,$valscurr,$sPat) ;
  768.  
  769.   $curr = 0 ;
  770.   $c = 'n' ;
  771.   $Xr = 0 ;
  772.   &unshowRecord ;
  773.  
  774.   while ( 1 )
  775.     { $valscurr = $vals[$curr] ;
  776.       @record = split(/$;/,$data2[$valscurr],$#name2+1) ;
  777.       $markInfo = ( defined $marked{$valscurr} ) ? '** MARKED **' : '' ;
  778.       &showJinxD($dirty) ;
  779.       &showRecord(*name2,*record,$#vals,$markInfo) ;
  780.       $c = &showChoice(*selectFromMenu,$c) ;
  781.  
  782.       if ( $c eq "x" )
  783.         { return('x') ; }
  784.       elsif ( $c eq "s" )
  785.         { return('a',$valscurr) ; }
  786.       elsif ( $c eq "S" )
  787.         { return('a', keys %marked ) ; }
  788.       elsif ( $c eq "t" )
  789.         { if ( defined $marked{$valscurr} )
  790.             { delete $marked{$valscurr} ; $dirty-- ; }
  791.           else
  792.             { $marked{$valscurr} = 1 ; $dirty++ ; }
  793.         }
  794.       elsif ( $c eq "T" )
  795.         { for $i ( @vals )
  796.         { if ( defined $marked{$i} )
  797.                 { delete $marked{$i} ; $dirty-- ; }
  798.               else
  799.                 { $marked{$i} = 1 ; $dirty++ ; }
  800.             }
  801.     }
  802.       elsif ( $c eq "C" )
  803.         { %marked = () ;
  804.       $dirty = 0 ;
  805.     }
  806.       elsif ( $c eq 'M' && $dirty > $#data2 )
  807.     { &showStatusBeep('every record is Marked') ; }
  808.       elsif ( $c eq 'M' )
  809.         { @record = &emptyRecord($#name2) ;
  810.           $Xr = 0 ;
  811.           $curF = 0 ;
  812.           &unshowValues ;
  813.           &showRecord(*name2,*record,$#data2) ;
  814.           if ( &doUpdate(*updReMenu,'user',1,
  815.                          *testRe,*quitUpdAdd,*retRe) eq 'a'
  816.              )
  817.             { &showStatus('Marking ...') ;
  818.               @unmarked = grep( ! defined $marked{$_}, @vals) ;
  819.               ($res,$res1) = &doAddMark(*data2,*record,*marked,*unmarked) ;
  820.               if ( $res )
  821.                 { &showStatus("marked ",$res1+0) ;
  822.                   $dirty += $res1 ;
  823.                 }
  824.               &showStatus($res1) if ! $res ;
  825.             }
  826.           else
  827.             { &showStatus('no change') ; }
  828.           &unshowValues ;
  829.         }
  830.       elsif ( $c eq 'U' && $dirty == 0 )
  831.     { &showStatusBeep('every record is Unmarked') ; }
  832.       elsif ( $c eq 'U' )
  833.         { @record = &emptyRecord($#name2) ;
  834.           $Xr = 0 ;
  835.           $curF = 0 ;
  836.           &unshowValues ;
  837.           &showRecord(*name2,*record,$#data2) ;
  838.           if ( &doUpdate(*updReMenu,'user',1,
  839.                          *testRe,*quitUpdAdd,*retRe) eq 'a'
  840.              )
  841.             { &showStatus('unMarking ...') ;
  842.               @sub = keys %marked ;
  843.               ($res,$res1) = &doDelMark(*data2,*record,*marked,*sub) ;
  844.               if ( $res )
  845.                 { &showStatus("unmarked ",0+$res1) ;
  846.                   $dirty -= $res1 ;
  847.                 }
  848.               &showStatus($res1) if ! $res ;
  849.             }
  850.           else
  851.             { &showStatus('no change') ; }
  852.           &unshowValues ;
  853.         }
  854.       elsif ( $c eq "n" && $curr < $#vals )
  855.         { $curr++ ;
  856.           &unshowValues ;
  857.         }
  858.       elsif ( $c eq "p" && $curr > 0 )
  859.         { $curr-- ;
  860.           &unshowValues ;
  861.         }
  862.       elsif ( $c eq "/" )
  863.         { ($res,$sPat) = &getStrFrom('pattern:',*sPat,$#sPat) ;
  864.           if ( $res == 0 || $sPat eq '' )
  865.         { &showStatus('no change') ; }
  866.           elsif ( ! &testPat($sPat) )
  867.         { &showStatus($@) ; }
  868.       else
  869.         { ($curr,$curF,$res) =
  870.         &searchNextIndRecord(*vals,*data2,$curr,*record,$curF,$sPat) ;
  871.               &showStatus($res) ;
  872.               &unshowValues if $res eq 'found' ;
  873.         }
  874.         }
  875.       elsif ( $c eq ";" && $#sPat >= 0 )
  876.         { $sPat = $sPat[$#sPat] ;
  877.           if ( ! &testPat($sPat) )
  878.         { &showStatus($@) ; }
  879.       else
  880.         { ($curr,$curF,$res) =
  881.         &searchNextIndRecord(*vals,*data2,$curr,*record,$curF,$sPat) ;
  882.               &showStatus($res) ;
  883.               &unshowValues if $res eq 'found' ;
  884.         }
  885.         }
  886.       elsif ( $c eq ">" && $dirty > ( defined $marked{$valscurr} ) )
  887.         { for ( $i=$curr+1 ;
  888.                 $i <= $#vals && ! defined $marked{$vals[$i]} ;
  889.                 $i++ ) { ; }
  890.           if ( $i > $#vals )
  891.             { &showStatusBeep('not found') ; }
  892.           else
  893.             { &showStatus('found') ;
  894.               $curr = $i ;
  895.               &unshowValues ;
  896.             }
  897.         }
  898.       elsif ( $c eq "<" && $dirty > ( defined $marked{$valscurr} ) )
  899.         { for ( $i=$curr-1 ; 
  900.                 $i >= 0 && ! defined $marked{$vals[$i]} ; 
  901.                 $i-- ) { ; }
  902.           if ( $i < 0 )
  903.             { &showStatusBeep('not found') ; }
  904.           else
  905.             { &showStatus('found') ;
  906.               $curr = $i ;
  907.               &unshowValues ;
  908.             }
  909.         }
  910.       elsif ( $c eq ")" && ($#vals - $dirty >= (!defined($marked{$valscurr}))) )
  911.         { for ( $i=$curr+1 ;
  912.                 $i <= $#vals && defined $marked{$vals[$i]} ;
  913.                 $i++ ) { ; }
  914.           if ( $i > $#vals )
  915.             { &showStatusBeep('not found') ; }
  916.           else
  917.             { &showStatus('found') ;
  918.               $curr = $i ;
  919.               &unshowValues ;
  920.             }
  921.         }
  922.       elsif ( $c eq "(" && ($#vals - $dirty >= (!defined($marked{$valscurr}))) )
  923.         { for ( $i=$curr-1 ; 
  924.                 $i >= 0 && defined $marked{$vals[$i]} ; 
  925.                 $i-- ) { ; }
  926.           if ( $i < 0 )
  927.             { &showStatusBeep('not found') ; }
  928.           else
  929.             { &showStatus('found') ;
  930.               $curr = $i ;
  931.               &unshowValues ;
  932.             }
  933.         }
  934.       elsif ( $c eq "g" && ($res = &getGoto($curr)) ne '' &&
  935.               0 <= $res && $res <= $#data2 )
  936.         { do { $curr = $res ; &unshowValues ; } if $res != $curr ;
  937.           $Xr = 0 ;
  938.         }
  939.       elsif ( $c eq 'KEY_UP' && $curF > 0 )
  940.         { $curF-- ; }
  941.       elsif ( $c eq 'KEY_DOWN' && $curF < $#name2 )
  942.         { $curF++ ; }
  943.       elsif ( $c eq 'KEY_LEFT' && $Xr > 0 )
  944.         { $Xr = &max($Xr-1-int(($numValueCols-2)/3),0) ; }
  945.       elsif ( $c eq 'KEY_RIGHT' && $Xr < $maxXr )
  946.         { $Xr = &min($Xr+1+int(($numValueCols-2)/3),$maxXr) ; }
  947.       else
  948.         { &showStatusBeep("can't do it") ; }
  949.    }
  950. }
  951.  
  952. sub doUpdate
  953.   { local(*menu,$modeDef,$acceptDef,*testUp,*quitUp,*retUp) = @_ ;
  954.     local($newField,$res,$c) ;
  955.     local($mode) = $modeDef ;
  956.     local(@accept) = @record ;
  957.     for (@accept) { $_ = $acceptDef ; }
  958.  
  959.     while (1)
  960.       { $newField = $record[$curF] if $res ne 'edit' ;
  961.         if ( $mode eq 'user' || $res eq 'edit' )
  962.           { &showMenu(*updateMenu) ;
  963.             &showCommand("now what:","edit, rule: $pat[$curF]") ;
  964.             ($newField,$res) = &editValue($newField,$curF) ;
  965.             $res = &ch2key($res) ;
  966.             &showStatus('') ;
  967.           }
  968.         else
  969.           { $res = 'KEY_TAB' ; }
  970.  
  971.         if ( $record[$curF] eq $newField && $accept[$curF] )
  972.           { ; }
  973.         elsif ( &testUp($newField,$curF,$res) )
  974.           { $record[$curF] = $newField ;
  975.             $accept[$curF] = $acceptDef ;
  976.           }
  977.         else
  978.           { &showStatusBeep($testUp) ;
  979.             $c = &showChoice(*menu,'e') ;
  980.             if ( $c eq "e" )
  981.               { $res = 'edit' ; }
  982.             elsif ( $c eq "a" )
  983.               { $record[$curF] = $newField ;
  984.                 $accept[$curF] = $acceptDef ;
  985.               }
  986.             elsif ( $c eq "f" )
  987.               { &showValue($record[$curF],$curF) ;
  988.                 if ( $accept[$curF] || &testUp($record[$curF],$curF) )
  989.                   { &showStatus('') ; }
  990.                 else
  991.                   { $newField = $record[$curF] ;
  992.                     $res = 'edit' ;
  993.                     &showStatusBeep("this isn't ok either") ;
  994.                   }
  995.               }
  996.             elsif ( $c eq "x" )
  997.               { $res = 'exit' ;
  998.                 &unshowValues ;
  999.               }
  1000.           }
  1001.  
  1002.         if ( $res eq 'exit' )
  1003.           { return('f') ; }
  1004.         elsif ( $res eq 'edit' )
  1005.           { ; }
  1006.         elsif ( &quitUp($res,$curF) )
  1007.           { $record = join($;,@record) ;
  1008.             $res = &retUp ;
  1009.             return $res if $res ne 'e' ;
  1010.             $res = 'edit' ;
  1011.           }
  1012.         elsif ( $res eq 'KEY_UP' && $curF > 0 )
  1013.           { $curF-- ; }
  1014.         elsif ( ( $res eq 'KEY_TAB' || $res eq 'KEY_RET' || $res eq 'KEY_DOWN' )
  1015.                 && $curF < $#name )
  1016.           { $curF++ ; }
  1017.         else
  1018.           { &beep ; }
  1019.       }
  1020.     continue
  1021.       { &showRecord(*name,*record,$#data) ; }
  1022.   }
  1023.  
  1024. sub testRe
  1025.   { local($value,$curF,$next) = @_ ;
  1026.     return 1 if &testPat($value) ;
  1027.     $testRe = $@ ;
  1028.     return 0 ;
  1029.   }
  1030.  
  1031. sub testEx
  1032.   { local($value,$curF,$next) = @_ ;
  1033.     if ( $value =~ /^\s+$/ )
  1034.       { $testEx = 'empty expression' ;
  1035.         return 0 ;
  1036.       }
  1037.     elsif ( ! &testExpr($value) )
  1038.       { $testEx = $@ ;
  1039.         return 0 ;
  1040.       }
  1041.     return 1 ;
  1042.   }
  1043.  
  1044. sub testNames
  1045.   { local($value,$curF,$next) = @_ ;
  1046.     if ( $value !~ /$namePat/ )
  1047.       { $testNames = "name '$value' not alfa-numeric" ;
  1048.         return 0 ;
  1049.       }
  1050.     local($res) ; $res = grep(/^$value$/,@record) ;
  1051.     if ( $res == 1 && $value ne $record[$curF] )
  1052.       { $testNames = "name '$value' is used" ;
  1053.         return 0 ;
  1054.       }
  1055.     return 1 ;
  1056.   }
  1057.  
  1058. sub testUpd
  1059.   { local($value,$curF,$next) = @_ ;
  1060.     return 1 if $pat[$curF] eq '' || $value =~ /$pat[$curF]/ ;
  1061.     $testUpd = "$name[$curF] doesn\'t match $pat[$curF]" ;
  1062.     return 0 ;
  1063.   }
  1064.  
  1065. sub quitUpd
  1066.   { local($next,$curF) = @_ ;
  1067.     return $next eq 'KEY_TAB' ;
  1068.   }
  1069.  
  1070. sub quitUpdAdd
  1071.   { local($next,$curF) = @_ ;
  1072.     return 1 if $next eq 'KEY_TAB' && $curF == $#name ;
  1073.     $mode = ($next eq 'KEY_TAB') ? 'system' : 'user' ;
  1074.     return 0 ;
  1075.   }
  1076.  
  1077. sub retRe
  1078.   { local($res) =
  1079.       ( grep(/./,@record) == 0 ) ? 'a' : &showChoice(*updateXMenu,'a') ;
  1080.     $mode = 'user' if $res eq 'e' ;
  1081.     return $res ;
  1082.   }
  1083.  
  1084. sub retEx
  1085.   { local($res) =
  1086.       ( grep(/./,@record) == 0 ) ? 'f' : &showChoice(*updateXMenu,'a') ;
  1087.     $mode = 'user' if $res eq 'e' ;
  1088.     return $res ;
  1089.   }
  1090.  
  1091. sub retNames
  1092.   { local($res) = ( $record eq join("$;",@name) )
  1093.                   ? 'f' : &showChoice(*updateXMenu,'a') ;
  1094.     return $res ;
  1095.   }
  1096.  
  1097. sub retPat
  1098.   { local($res) ;
  1099.     if ( $record eq join("$;",@pat) )
  1100.       { $res = 'f' ; }
  1101.     else
  1102.       { $res = &showChoice(*updateXMenu,'a') ;
  1103.         &unshowValues if $res eq 'f' ;
  1104.       }
  1105.     return $res ;
  1106.   }
  1107.  
  1108. sub retUpd
  1109.   { if ( $data[$curr] eq $record )
  1110.       { return 'f' ; }
  1111.     else
  1112.       { local($res) = &showChoice(*updateXMenu,'a') ;
  1113.         &unshowValues if $res eq 'f' ;
  1114.         return $res ;
  1115.       }
  1116.   }
  1117.  
  1118. sub retUpdAdd
  1119.   { local($res) = &showChoice(*updateXMenu,'a') ;
  1120.     &unshowValues if $res eq 'f' ;
  1121.     $mode = 'user' if $res eq 'e' ;
  1122.     return $res ;
  1123.   }
  1124.  
  1125. sub doMetaMenu
  1126. { local($c,$command,$Yd,$Xd,$Yr,$Xr,$curr,$maxStrlenD,$str,$res,$found) ;
  1127.   local($colView) ;
  1128.   $c = ($#name < 0) ? 'o' : 'i' ;
  1129.  
  1130.   &unshowData ;
  1131.  
  1132.   while( 1 )
  1133.     { $found = 1 ;
  1134.  
  1135.       &showJinxD() ;
  1136.       &showData(*data,$#name) ;
  1137.       $c = &showChoice(*metaMenu,$c) ;
  1138.       $command = $metaMenu{$c} ;
  1139.  
  1140.       if ( $c eq '' || $c eq "Q" )
  1141.         { &quit('no update') if $dirty ;
  1142.           &quit('normal quit') ;
  1143.         }
  1144.       elsif ( $c eq "q" && &checkDirty )
  1145.         { &quit('jinx says: bye bye') ; }
  1146.       elsif ( $c eq "p" && $curr > 0 )
  1147.         { $curr = &max(0,$curr-$numFieldLines) ; }
  1148.       elsif ( $c eq "n" && $curr < $#data )
  1149.         { $curr = &min($#data, $curr + $numFieldLines) ; }
  1150.       elsif ( $c eq 'KEY_UP' && $curr > 0 )
  1151.         { $curr-- ; }
  1152.       elsif ( $c eq 'KEY_DOWN' && $curr < $#data )
  1153.         { $curr++ ; }
  1154.       elsif ( $c eq 'KEY_LEFT' && $Xd > 0 )
  1155.         { if ( $colView )
  1156.         { $Xd-- ; }
  1157.       else
  1158.         { $Xd = &max($Xd-1-int(($numInfoCols-2)/3),0) ; } 
  1159.     }
  1160.       elsif ( $c eq 'KEY_RIGHT' && $Xd < $maxXd )
  1161.         { if ( $colView )
  1162.         { $Xd++ ; }
  1163.       else
  1164.         { $Xd = &min($Xd+1+int(($numInfoCols-2)/3),$maxXd) ; }
  1165.     }
  1166.       elsif ( $c eq "g" && $#name >= 0 && $#data >= 0 &&
  1167.               ( $res = &getGoto($curr) ) ne '' &&
  1168.               0 <= $res && $res <= $#data )
  1169.         { $curr = $res ;
  1170.           $Xd = 0 ;
  1171.         }
  1172.       elsif ( $c eq "/" && $#name >= 0 && $#data >= 0)
  1173.         { ($res,$sPat) = &getStrFrom('pattern:',*sPat,$#sPat) ;
  1174.           if ( $res == 0 || $sPat eq '' )
  1175.         { &showStatus('no change') ; }
  1176.           elsif ( ! &testPat($sPat) )
  1177.         { &showStatus($@) ; }
  1178.       else
  1179.         { ($curr,$res) = &searchNext(*data,$curr,$sPat) ;
  1180.               &showStatus($res) ;
  1181.         }
  1182.         }
  1183.       elsif ( $c eq ";" && $#name >= 0 && $#data >= 0 && $#sPat >= 0 )
  1184.         { $sPat = $sPat[$#sPat] ;
  1185.           if ( ! &testPat($sPat) )
  1186.         { &showStatus($@) ; }
  1187.       else
  1188.         { ($curr,$res) = &searchNext(*data,$curr,$sPat) ;
  1189.               &showStatus($res) ;
  1190.         }
  1191.         }
  1192.       elsif ( $c eq "i" && $#name >= 0 )
  1193.         { $curr = &doMainMenu(*name,*pat,*data,*ddata,$curr,*mainUpdate) ;
  1194.           if ( $curr eq 'Q' )
  1195.             { if ( $dirty ) 
  1196.                 { &quit('quit without update') ; }
  1197.               else
  1198.                 { &quit("normal quit ; database wasn't modified") ; }
  1199.             }
  1200.           $curr = &min($#data,$curr) ;
  1201.           $Xd = 0 ;
  1202.         }
  1203.       elsif ( $c eq "o" && &checkDirty )
  1204.         { ($res,$str) = &getStrFrom('open:',*openAs,$currDb) ;
  1205.           if ( $res == 0 )
  1206.             { &showStatus('no change') ; }
  1207.           else
  1208.             { $currDb++ ;
  1209.               &showStatus('opening ...') ;
  1210.               ($res,@res) = &openCurrDb($str) ;
  1211.               &showStatusList(@res) ;
  1212.               if ( $res )
  1213.                 { push(@saveAs,$str)
  1214.             if $#saveAs < 0 || $saveAs[$#saveAs] ne $str ;
  1215.           $curr = &min($#data,0) ;
  1216.                   $Xd = 0 ;
  1217.                   $dirty = 0 ;
  1218.                   @ddata = () ;
  1219.                   &unshowData ;
  1220.                 }
  1221.             }
  1222.         }
  1223.       elsif ( $c eq "C" && $#name >= 0 && &checkDirty )
  1224.         { &showStatus("database wasn't modified") if ! $dirty ;
  1225.           $dirty = 0 ;
  1226.           $db = '' ;
  1227.           @data = () ;
  1228.           @ddata = () ;
  1229.           @descr = () ;
  1230.           @name = () ;
  1231.           @pat = () ;
  1232.           $curr = -1 ;
  1233.           &unshowData ;
  1234.         }
  1235.       elsif ( $c eq "V" )
  1236.     { $colView = ! $colView ;
  1237.       $Xd = 0 ;
  1238.       &unshowData ;
  1239.     }
  1240.       else { $found = 0 ; }
  1241.  
  1242.       if ( $found )
  1243.         { ; }
  1244.       elsif ( $c eq "s" && $#name >= 0)
  1245.         { if ( $db eq '' )
  1246.             { &showStatus('no name ; Save as ...') ; }
  1247.           elsif ( ! $dirty)
  1248.             { &showStatus('no modification since last update') ; }
  1249.           else
  1250.             { &showStatus('saving ...') ;
  1251.               ($res,$resStr) = &putInfo(*data,$db,'dat') ;
  1252.               &showStatus($resStr) ;
  1253.               $dirty = 0 if $res ;
  1254.             }
  1255.         }
  1256.       elsif ( $c eq "S" && $#name >= 0)
  1257.         { push(@saveAs,'') ;
  1258.           ($res,$str) = &getStrFrom('Save as:',*saveAs) ;
  1259.           if ( $res == 0 || $str eq '')
  1260.             { &showStatus('not saved') ; }
  1261.           else
  1262.             { if ( ! -e "$str.des" ||
  1263.                    do { &showStatusBeep("database $str exists") ;
  1264.                         &showChoice(*dirtyMenu,'n','write anyway ?') eq 'y' ;
  1265.                       }
  1266.                  )
  1267.                { &showStatus('Saving ...') ;
  1268.                  ($resDescr,$resStr1) = &putInfo(*descr,$str,'des') ;
  1269.                  if ( ! $resDescr )
  1270.                    { &showStatus($resStr1) ; }
  1271.          else
  1272.            { ($resData, $resStr2) = &putInfo(*data,$str,'dat') ;
  1273.                      &showStatus("$resStr1; $resStr2") ;
  1274.                      if ( $db eq '' && $resData )
  1275.                        { $db = $str ;
  1276.                          $dirty = 0 ;
  1277.                        }
  1278.            }
  1279.                }
  1280.              else
  1281.                { &showStatus("no change") ; }
  1282.             }
  1283.         }
  1284.       elsif ( $c eq "O" && $#name >= 0 && $#data >= 0 )
  1285.         { &showStatus('select sort keys (or finish to sort whole records)') ;
  1286.       ($res,@record) = &getNames(*name) ;
  1287.           if ( $res )
  1288.             { &showStatus('sorting ...') ;
  1289.               ($res,@errors) = &doSort(*data,*record) ;
  1290.               &showStatusList(@errors) ;
  1291.               do { $curr = 0 ; $dirty++ ; } if $res ;
  1292.             }
  1293.           else
  1294.             { &showStatus('no change') ; }
  1295.         }
  1296.       elsif ( $c eq "P" && $#name >= 0 && &checkDirty )
  1297.         { &showStatus('select columns to project') ;
  1298.       ($res,@record) = &getNames(*name) ;
  1299.           if ( $res )
  1300.             { &showStatus('Projecting ...') ;
  1301.               ($res,@errors) = &doProject(*descr,*name,*pat,*data,*record) ;
  1302.               &showStatusList(@errors) ;
  1303.                 if ( $res )
  1304.                   { &doProjectData(*ddata,*record) ;
  1305.             $dirty = 1 ;
  1306.                     $curr = &min($#data,0) ;
  1307.                     $Xd = 0 ;
  1308.                     $db = '' ;
  1309.                   }
  1310.             }
  1311.           else
  1312.             { &showStatus('no change') ; }
  1313.         }
  1314.       elsif ( $c eq "D" && $#name >= 0 )
  1315.         { @record = @pat ;
  1316.           $Xr = 0 ;
  1317.           $curF = 0 ;
  1318.           &showRecord(*name,*record,$#data) ;
  1319.           @descr1 = @descr ;
  1320.           if ( &doDescrMenu(*descr1) &&
  1321.                join("$;",sort @descr) ne join("$;",sort @descr1) )
  1322.             { @descr = @descr1 ;
  1323.               &splitDescr(*descr,*name,*pat) ;
  1324.               $db = '' ;
  1325.             }
  1326.           else
  1327.             { &showStatus('no change') ; }
  1328.         }
  1329.       elsif ( $c eq "J" && $#name >= 0 && &checkDirty )
  1330.         { ($res,$str) = &getStrFrom('with what?',*joinList,$#joinList) ;
  1331.           if ( $res == 0 )
  1332.             { &showStatus('no change') ; }
  1333.           else
  1334.             { &showStatus('opening ...') ; 
  1335.               ($res,@errors) = &openDb($str,*descr2,*data2,*name2,*pat2) ;
  1336.               &showStatusList(@errors) ;
  1337.               if ( $res == 0 )
  1338.                 { if ( $Xd )
  1339.                     { $Xd = 0 ;
  1340.                       &unshowData ;
  1341.                     }
  1342.                   &showStatus('Joining ...') ;
  1343.                   ($res,@errors) = &doJoin(*descr,*data,*descr2,*data2,'','') ;
  1344.                   &showStatusList(@errors) ;
  1345.                   if ( $res )
  1346.                     { &splitDescr(*descr,*name,*pat) ;
  1347.                       $dirty = 1 ;
  1348.                       @ddata = () ;
  1349.                       $curr = &min($#data,0) ;
  1350.                       $db = '' ;
  1351.                       &unshowData ;
  1352.                     }
  1353.                 }
  1354.             }
  1355.         }
  1356.       elsif ( $c eq "G" && &checkDirty )
  1357.         { if ( do { ($res,$str) =
  1358.                        &getStrFrom('Guess what?',*guessList,$#guessList) ;
  1359.                     $res == 0 || $str eq '' ;
  1360.                   } 
  1361.              )
  1362.             { &showStatus('no change') ; }
  1363.           elsif ( ! -r $str )
  1364.             { &showStatus("can't read $str") ; }
  1365.           elsif ( do { ($res,$pat) =
  1366.                           &getStrFrom('separator?',*sepList,$#sepList) ;
  1367.                         $res == 0 ;
  1368.                      } 
  1369.                 )
  1370.             { &showStatus('no change') ; }
  1371.           else
  1372.             { &showStatus('Guessing ...') ;
  1373.               ($res,$errors) = &doGuessData($str,$pat,*data1) ;
  1374.               &showStatus($errors) ;
  1375.               if ( $res != 0 )
  1376.                 { @data = @data1 ;
  1377.                   &doGuessDescr($res,*descr) ;
  1378.                   &splitDescr(*descr,*name,*pat) ;
  1379.                   $dirty = 1 ;
  1380.                   @ddata = () ;
  1381.                   $curr = &min($#data,0) ;
  1382.                   $Xd = 0 ;
  1383.                   $db = '' ;
  1384.                   &unshowData ;
  1385.                 }
  1386.             }
  1387.         }
  1388.       elsif ( $c eq "A" && &checkDirty )
  1389.         { ($fName,$fPat,$fVal) = &getFieldDescr ;
  1390.           if ( $fName ne '' )
  1391.             { &showStatus('Adding ...') ;
  1392.               &addFieldNP(*descr,*name,$fName,*pat,$fPat) ;
  1393.               for $data ( @data )  { $data .= $; . $fVal ; }
  1394.               for $data ( @ddata ) { $data .= $; . $fVal ; }
  1395.               $db = '' ;
  1396.               $dirty++ ;
  1397.               &unshowData ;
  1398.             }
  1399.           else
  1400.             { &showStatusBeep($fPat) ; }
  1401.         }
  1402.       elsif ( $c eq "T" && $#data >= 0 )
  1403.         { &showStatus('Testing All ...') ;
  1404.           $curr = &showTestAll(*name,*pat,*data) ;
  1405.         }
  1406.       elsif ( $checkDirty )
  1407.         { $checkDirty = 0 ; }
  1408.       else
  1409.         { &showStatusBeep("can't do it") ; }
  1410.     }
  1411. }
  1412.  
  1413. sub mainUpdate
  1414.   { local($c) = @_ ;
  1415.     if ( $c eq 'u' )
  1416.       { &doUpdate(*updMatchMenu,'user',1,*testUpd,*quitUpd,*retUpd) ; }
  1417.     else
  1418.       { &doUpdate(*updMatchMenu,'user',0,*testUpd,*quitUpdAdd,*retUpdAdd) ; }
  1419.   }
  1420.  
  1421. sub doMainMenu
  1422. { local(*name,*pat,*data,*ddata,$curr,*onUpdate) = @_ ;
  1423.  
  1424.   local($c,$Yr,$Xr,$maxStrlenR,$curF,@ndata,$found) ;
  1425.  
  1426.   $curr = -1 if $#data < 0 ;
  1427.   $c = ( $#data < 0 ) ? 'a' : 'n' ;
  1428.   $Xr = 0 ;
  1429.   &unshowRecord ;
  1430.  
  1431.   while ( 1 )
  1432.     { $found = 1 ;
  1433.       if ($curr >= 0)
  1434.         { @record = split(/$;/,$data[$curr],$#name+1) ; }
  1435.       else
  1436.         { @record = &emptyRecord($#name) ; }
  1437.       &showJinxD() ;
  1438.       &showRecord(*name,*record,$#data) ;
  1439.       $c = ( $#data == -1 ) ? 'a' : 'n' if $c eq 'd' ;
  1440.       $c = &showChoice(*mainMenu,$c)  ;
  1441.  
  1442.       if ( $c eq '' || $c eq "Q" )
  1443.         { return('Q') ; }
  1444.       elsif ( $c eq "f" )
  1445.         { return($curr) ; }
  1446.       elsif ( $c eq "g" && $#data >= 0 &&
  1447.               ($res = &getGoto($curr)) ne '' &&
  1448.               0 <= $res && $res <= $#data )
  1449.         { do { $curr = $res ; &unshowValues ; } if $res != $curr ;
  1450.           $Xr = 0 ;
  1451.         }
  1452.       elsif ( $c eq 'KEY_UP' && $curF > 0 )
  1453.         { $curF-- ; }
  1454.       elsif ( $c eq 'KEY_DOWN' && $curF < $#name )
  1455.         { $curF++ ; }
  1456.       elsif ( $c eq 'KEY_LEFT' && $Xr > 0 )
  1457.         { $Xr = &max($Xr-1-int(($numValueCols-2)/3),0) ; }
  1458.       elsif ( $c eq 'KEY_RIGHT' && $Xr < $maxXr )
  1459.         { $Xr = &min($Xr+1+int(($numValueCols-2)/3),$maxXr) ; }
  1460.       elsif ( $c eq "n" && $curr < $#data )
  1461.         { $curr++ ;
  1462.           &unshowValues ;
  1463.         }
  1464.       elsif ( $c eq "p" && $curr > 0)
  1465.         { $curr-- ;
  1466.           &unshowValues ;
  1467.         }
  1468.       elsif ( $c eq "/" && $curr >= 0 )
  1469.         { ($res,$sPat) = &getStrFrom('pattern:',*sPat,$#sPat) ;
  1470.           if ( $res == 0 || $sPat eq '' )
  1471.         { &showStatus('no change') ; }
  1472.           elsif ( ! &testPat($sPat) )
  1473.         { &showStatus($@) ; }
  1474.       else
  1475.         { ($curr,$curF,$res) =
  1476.         &searchNextRecord(*data,$curr,*record,$curF,$sPat) ;
  1477.               &showStatus($res) ;
  1478.               &unshowValues if $res eq 'found' ;
  1479.         }
  1480.         }
  1481.       elsif ( $c eq ";" && $curr >= 0 && $#sPat >= 0 )
  1482.         { $sPat = $sPat[$#sPat] ;
  1483.           if ( ! &testPat($sPat) )
  1484.         { &showStatus($@) ; }
  1485.       else
  1486.         { ($curr,$curF,$res) =
  1487.         &searchNextRecord(*data,$curr,*record,$curF,$sPat) ;
  1488.               &showStatus($res) ;
  1489.               &unshowValues if $res eq 'found' ;
  1490.         }
  1491.         }
  1492.       else
  1493.         { $found = 0 ; }
  1494.       
  1495.       if ( $found )
  1496.         { ; }
  1497.       elsif ( $c eq "u" && $curr >= 0 )
  1498.         { $Xr = 0 ;
  1499.           &showRecord(*name,*record,$#data) ;
  1500.           if ( &onUpdate($c) eq 'a' )
  1501.             { $data[$curr] = $record ;
  1502.               $dirty++ ;
  1503.             }
  1504.           else
  1505.             { &showStatus('no change') ; }
  1506.         }
  1507.       elsif ( $c eq "a" || ( $c eq "c" && $curr >= 0 ) )
  1508.         { if ( $c eq "a" )
  1509.             { @record = &emptyRecord($#name) ;
  1510.               &unshowValues ;
  1511.             }
  1512.           $Xr = 0 ;
  1513.           $oldF = $curF ;
  1514.           $curF = 0 ;
  1515.           $curr++ ;
  1516.           &showRecord(*name,*record,$#data) ;
  1517.           if ( &onUpdate($c) eq 'a' )
  1518.             { splice(@data,$curr,0,join($;,@record)) ;
  1519.               $dirty++ ;
  1520.             }
  1521.           else
  1522.             { &showStatus('no change') ;
  1523.               $curr-- ;
  1524.             }
  1525.           $curF = $oldF ;
  1526.         }
  1527.       elsif ( $c eq "d" && $curr >= 0 )
  1528.         { push(@ddata,splice(@data,$curr,1)) ;
  1529.           &showStatus('deleted') ;
  1530.           $curr = &min($curr,$#data) ;
  1531.           $dirty++ ;
  1532.           &unshowValues ;
  1533.         }
  1534.       elsif ( $c eq "t" && $curr >= 0 )
  1535.         { &showTest(*name,*pat,*record) ; }
  1536.       elsif ( $c eq "T" && $curr >= 0 )
  1537.         { &showStatus('Testing All ...') ;
  1538.           $curr = &showTestAll(*name,*pat,*data) ;
  1539.           &unshowValues ;
  1540.         }
  1541.       elsif ( $c eq "O" && $#data >= 0 )
  1542.         { &showStatus('select sort keys (or finish to sort whole records)') ;
  1543.           ($res,@record) = &getNames(*name) ;
  1544.           if ( $res )
  1545.             { &showStatus('sorting ...') ;
  1546.               &unshowValues ;
  1547.               ($res,@errors) = &doSort(*data,*record) ;
  1548.               &showStatusList(@errors) ;
  1549.               do { $curr = 0 ; $dirty++ ; } if $res ;
  1550.             }
  1551.           else
  1552.             { &showStatus('no change') ; }
  1553.           &unshowValues ;
  1554.         }
  1555.       elsif ( $c eq "U" && $#ddata >= 0 )
  1556.         { @vals = 0..$#ddata ;
  1557.           ($res,@vals) = &selectFrom('deleted records',*name,*ddata,@vals) ;
  1558.           if ( $res eq 'x' )
  1559.             { &showStatus("no change") ; }
  1560.           elsif ( $res eq 'a' )
  1561.             { if ( $#vals >= 0 )
  1562.                 { $dirty++ ;
  1563.                   for $vals ( reverse sort byNum @vals )
  1564.                     { push(@data,splice(@ddata,$vals,1)) ; } 
  1565.                   $curr = &min(0,$#data) ;
  1566.                   &showStatus('undeleted ', $#vals+1) ;
  1567.                 }
  1568.               else
  1569.                 { &showStatus("no change") ; }
  1570.             }
  1571.           &unshowValues ;
  1572.         }
  1573.       elsif ( $c eq "E" && $#data >= 0 )
  1574.         { @vals = 0..$#data ;
  1575.           ($res,@vals) = &selectFrom('records to extract',*name,*data,@vals) ;
  1576.           if ( $res eq 'x' )
  1577.             { &showStatus("no change") ; }
  1578.           elsif ( $res eq 'a' )
  1579.             { if ( $#vals >= 0 )
  1580.                 { $dirty++ ;
  1581.                   $db = '' ;
  1582.                   @ndata = () ;
  1583.                   for $vals ( reverse sort byNum @vals )
  1584.                     { push(@ndata,splice(@data,$vals,1)) ; } 
  1585.                   $curr = &min(0,$#data) ;
  1586.                   @ddata = (@ddata,@data) ;
  1587.                   @data = @ndata ;
  1588.                   &showStatus('extracted ', $#vals+1) ;
  1589.                 }
  1590.               else
  1591.                 { &showStatus("no change") ; }
  1592.             }
  1593.           &unshowValues ;
  1594.         }
  1595.       elsif ( $c eq "D" && $#data >= 0 )
  1596.         { @vals = 0..$#data ;
  1597.           ($res,@vals) = &selectFrom('records to delete',*name,*data,@vals) ;
  1598.           if ( $res eq 'x' )
  1599.             { &showStatus("no change") ; }
  1600.           elsif ( $res eq 'a' )
  1601.             { if ( $#vals >= 0 )
  1602.                 { $dirty++ ;
  1603.                   $curr = &min(0,$#data) ;
  1604.                   for $vals ( reverse sort byNum @vals )
  1605.                     { push(@ddata,splice(@data,$vals,1)) ; } 
  1606.                   &showStatus('deleted ', $#vals+1) ;
  1607.                 }
  1608.               else
  1609.                 { &showStatus("no change") ; }
  1610.             }
  1611.           &unshowValues ;
  1612.         }
  1613.       elsif ( $c eq "P" )
  1614.         { ($res,$str) = &getStrFrom('at ?',*joinList,$#joinList) ;
  1615.           if ( $res == 0 )
  1616.             { &showStatus('no change') ; }
  1617.           else
  1618.             { &showStatus('opening ...') ; 
  1619.               ($res,@errors) = &openDb($str,*descr1,*data1,*name1,*pat1) ;
  1620.               &showStatusList(@errors) ;
  1621.               if ( $res == 0 )
  1622.                 { &showStatus('Peeking ...') ;
  1623.                   ($res,@errors) = &doPeek(*name,$str,*name1,*data1) ;
  1624.                   &showStatusList(@errors) ;
  1625.                   $dirty++ if $res ;
  1626.                   &unshowRecord ;
  1627.                 }
  1628.             }
  1629.         }
  1630.       elsif ( $c eq "R" )
  1631.         { ($res,$str) = &getStrFrom('what ?',*joinList,$#joinList) ;
  1632.           if ( $res == 0 )
  1633.             { &showStatus('no change') ; }
  1634.           else
  1635.             { &showStatus('opening ...') ; 
  1636.               ($res,@errors) = &openDb($str,*descr1,*data1,*name1,*pat1) ;
  1637.               &showStatusList(@errors) ;
  1638.               if ( $res == 0 )
  1639.                 { &showStatus('Reading ...') ;
  1640.                   ($res,@errors) = &doRead(*name,$str,*name1,*data1) ;
  1641.                   &showStatusList(@errors) ;
  1642.                   $dirty++ if $res ;
  1643.                   &unshowRecord ;
  1644.                 }
  1645.             }
  1646.         }
  1647.       elsif ( $c eq "K" && $#data >= 0 )
  1648.         { &showStatus('select key fields (or finish to uniq records)') ;
  1649.           ($res,@record) = &getNames(*name) ;
  1650.           if ( $res )
  1651.             { $res1 = ( $#record < 0 )
  1652.         ? 'record' : 'key (' . join(',',@name[@record]) . ')' ;
  1653.           &showStatus("Checking if every $res1 is uniq ...") ;
  1654.               ($res,@vals) = &doKeyTest(*data,*name,*record) ;
  1655.           if ( $res && $#vals >= 0 )
  1656.                 { $dirty++ ;
  1657.                   $curr = &min(0,$#data) ;
  1658.                   for $vals ( reverse sort byNum @vals )
  1659.                     { push(@ddata,splice(@data,$vals,1)) ; } 
  1660.                   &showStatus('deleted: ',$#vals+1,' record',$#vals?'s':'') ;
  1661.                 }
  1662.           elsif ( $res == 1 )
  1663.                 { &showStatus("every $res1 is unique") ; }
  1664.           elsif ( $res == 2 )
  1665.                 { &showStatus("one $res1 was not uniq") ; }
  1666.           elsif ( $res )
  1667.                 { &showStatus("$res1 was not uniq in ",$res-1,' cases') ; }
  1668.               else
  1669.                 { &showStatus("no change") ; }
  1670.             }
  1671.           else
  1672.             { &unshowValues ;
  1673.           &showStatus("no change") ;
  1674.         }
  1675.         }
  1676.       elsif ( $c eq "C" && $#data >= 0 )
  1677.         { @record = () ;
  1678.       for ( @name ) { push(@record,$fieldExpr{$_}) ; }
  1679.       $Xr = 0 ;
  1680.       $curF = 0 ;
  1681.           &unshowValues ;
  1682.           &showRecord(*name,*record,$#data) ;
  1683.           $res = &doUpdate(*updReMenu,'user',1,*testEx,*quitUpd,*retEx) ;
  1684.           if ( $res eq 'a' )
  1685.             { $i = 0 ;
  1686.           for ( @name ) { $fieldExpr{$_} = $record[$i] if $record[$i] ; }
  1687.           continue { $i++ ; }
  1688.           &showStatus("Computing ...") ;
  1689.           $res = &doCompute(*name,*record,*data,*ddata) ;
  1690.               $dirty++ ;
  1691.           &showStatus($res) ;
  1692.         }
  1693.           else
  1694.             { &showStatus('no change') ; }
  1695.       &unshowValues ;
  1696.     }
  1697.       else
  1698.         { &showStatusBeep("can't do it") ; }
  1699.     }
  1700. }
  1701.  
  1702. unshift(@INC,pop(@INC)) ;
  1703. do 'jinx.pl'   || die "can't include jinx.pl\n" ;
  1704. do 'curses.pl' || die "can't include curses.pl\n" ;
  1705. do 'cterm.pl'  || die "can't include cterm.pl\n" ;
  1706. do 'screen.pl' || die "can't include screen.pl\n" ;
  1707. do 'menus.pl'  || die "can't include menus.pl\n" ;
  1708.  
  1709. $inJinx = 1 ;
  1710.  
  1711. if ( $#ARGV == 0 && $ARGV[0] eq '-D' )
  1712.   { &initMenus($COLSdefault) ;
  1713.     close(STDIN) ;
  1714.     close(STDOUT) ;
  1715.     close(STDERR) ;
  1716.     $dumped = 1 ;
  1717.     dump startHere ;
  1718.   }
  1719.  
  1720. startHere: ;
  1721.  
  1722. if ( $dumped )
  1723.   { open(TTY,'>/dev/tty') || warn "warning: can't open /dev/tty\n" ;
  1724.     open(STDIN, '<&0') || do { print TTY "can't reopen STDIN"  ; exit ; } ;
  1725.     open(STDOUT,'>&1') || do { print TTY "can't reopen STDOUT" ; exit ; } ;
  1726.     open(STDERR,'>&2') || do { print TTY "can't reopen STDERR" ; exit ; } ;
  1727.     close(TTY) ;
  1728.   }
  1729.  
  1730. @ctermArgs = () ; $logLevel = -1 ;
  1731. while ( $#ARGV >= 0 && $ARGV[0] =~ /^-/ )
  1732.   { $OPT = shift ;
  1733.     $OPT =~ s/^.// ;
  1734.     if ( $OPT =~ /^L(\d+)?$/ )
  1735.       { $logLevel = $1 ;
  1736.         $logFile = shift ;
  1737.         push(@ctermArgs,$logFile,$logLevel) if $logFile ;
  1738.       }
  1739.     elsif ( $OPT eq 'D' )
  1740.       { ; }
  1741.     elsif ( $OPT eq 'v' )
  1742.       { print STDERR "Jinx version 2.1, " ;
  1743.         print STDERR "copyright (c) 1990, Henk P. Penning.\n" ;
  1744.     print STDERR "Jinx may be copied only under the terms " ;
  1745.     print STDERR "of the GNU General Public License,\na copy " ;
  1746.     print STDERR "of which comes with the Jinx distribution kit.\n" ;
  1747.     exit(0) ;
  1748.       }
  1749.     else
  1750.       { push(@errors,"$0: unknown option '$OPT'") ; }
  1751.   }
  1752.  
  1753. for ( @errors )
  1754.   { print STDERR "$_\n" ; }
  1755. die "Usage: $0 [-L[level] log-file] [ db ... ]\n" if $#errors >= 0 ;
  1756.  
  1757. ($res,@errors) = &testCurses ;
  1758.  
  1759. if ( ! $res )
  1760.   { print STDERR join("\n",@errors), "\n" ;
  1761.     exit ;
  1762.   }
  1763.  
  1764. &startCterm('-X',@ctermArgs) ;
  1765.  
  1766. &initCurses ;
  1767. &initEdit($curcon{'KEY_UP'}, $curcon{'KEY_DOWN'},
  1768.           $curcon{'KEY_RET'}, $curcon{'KEY_TAB'},
  1769.       14,16) ;
  1770. if ( defined $curfun{'editl'} )
  1771.   { &editl(2) ;
  1772.     &editl($curcon{'KEY_LEFT'}) if defined $curcon{'KEY_LEFT'} ;
  1773.   }
  1774. if ( defined $curfun{'editr'} )
  1775.   { &editr(6) ;
  1776.     &editr($curcon{'KEY_RIGHT'}) if defined $curcon{'KEY_RIGHT'} ;
  1777.   }
  1778. $keymap{2}   = 'KEY_LEFT' ;
  1779. $keymap{6}   = 'KEY_RIGHT' ;
  1780. $keymap{14}  = 'KEY_DOWN' ;
  1781. $keymap{16}  = 'KEY_UP' ;
  1782. $keymap{'h'} = 'KEY_LEFT' ;
  1783. $keymap{'l'} = 'KEY_RIGHT' ;
  1784. $keymap{'j'} = 'KEY_DOWN' ;
  1785. $keymap{'k'} = 'KEY_UP' ;
  1786. $flushOn = 1 ;
  1787.  
  1788. &addlog("open jinx:") ;
  1789. &addlog("logfile: " . ( $logFile ? "$logFile, level $logLevel" : 'none') ) ;
  1790.  
  1791. @ddata = () ;
  1792. @descr1 = () ;
  1793. @data1 = () ;
  1794. @descr2 = () ;
  1795. @data2 = () ;
  1796. @name1 = () ;
  1797. @name2 = () ;
  1798. @pat = () ;
  1799. @pat1 = () ;
  1800. @pat2 = () ;
  1801. %fieldExpr = () ;
  1802.  
  1803. &initMenus($COLS) unless $dumped && $COLS == $COLSdefault ;
  1804. &initParamsScreen ;
  1805. &QUIT('Get a wider terminal.') if $numInfoCols < 6 ;
  1806. &QUIT('Get a larger terminal.') if $numFieldLines < 1 ;
  1807.  
  1808. if ( $#ARGV < 0 )
  1809.   { @openAs = () ;
  1810.     @descr = () ;
  1811.     @name = () ;
  1812.     @pat = () ;
  1813.   }
  1814. else
  1815.   { @openAs = (@ARGV,'') ;
  1816.     $str = $ARGV[$currDb++] ;
  1817.     ($res,@res) = &openCurrDb($str) ;
  1818.     &showStatusList(@res) ;
  1819.   }
  1820.  
  1821. &doMetaMenu ;
  1822.  
  1823. &quit() ;
  1824.